﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Common.Cryptography
{
    public static class AESHelper
    {

        #region Global Var(s)

        private static Aes AesEninge = null;
        private const int AesKeySize = 32;
        private const int AesIVSize = 16;
        //internal static byte[] key = null;
        //internal static byte[] iv = null;

        private static MemoryStream ms = null;
        private static CryptoStream cs = null;
        private static BinaryWriter bw = null;
        private static BinaryReader br = null;

        #endregion

        #region Basic

        public static void init(string strPassword)
        {
            if (String.IsNullOrEmpty(strPassword))
                throw new ArgumentNullException("strPassword");

            AesEninge = Aes.Create();
            AesEninge.Key = CryptographyHelper.GetKeyFromPassword(strPassword, AesKeySize);
            AesEninge.IV = CryptographyHelper.GetKeyFromPassword(strPassword, AesIVSize);
        }

        public static void free()
        {
            if (AesEninge.isNull() == false)
                AesEninge.Clear();
        }

        #endregion

        #region Encryption

        public static byte[] EncryptedData { get; set; }

        public static void BeginEncrypt()
        {
            ICryptoTransform ct = AesEninge.CreateEncryptor(AesEninge.Key, AesEninge.IV);

            Clear();

            ms = new MemoryStream();
            cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
            bw = new BinaryWriter(cs);
        }

        public static int Write(byte[] RawData, int Offset, int Len)
        {
            if (AesEninge.isNull())
                throw new ArgumentNullException("Must call init frist!");
            if (RawData.isNull() || RawData.Length < 1)
                throw new ArgumentNullException("RawData");
            if (Len > RawData.Length)
                throw new ArgumentOutOfRangeException("Len");

            try
            {
                bw.Write(RawData, Offset, Len);
            }
            catch
            {
                return 0;
            }

            return ms.Length.ToInt();
        }

        public static void EndEncrypt()
        {
            bw.Close();
            bw.Dispose();

            cs.Close();
            cs.Dispose();

            ms.Close();

            EncryptedData = ms.ToArray();

            ms.Dispose();
        }

        #endregion

        #region Decryption

        public static byte[] DecryptedData { get; set; }

        public static void BeginDecrypt()
        {
            ICryptoTransform ct = AesEninge.CreateDecryptor(AesEninge.Key, AesEninge.IV);

            Clear();

            ms = new MemoryStream();
            cs = new CryptoStream(ms, ct, CryptoStreamMode.Read);
            br = new BinaryReader(cs);
        }

        public static void Read(byte[] RawData, int Offset, int Len)
        {
            if (AesEninge.isNull())
                throw new ArgumentNullException("Must call init frist!");
            if (RawData.isNull() || RawData.Length < 1)
                throw new ArgumentNullException("RawData");
            if (Len > RawData.Length)
                throw new ArgumentOutOfRangeException("Len");

            try
            {
                br.Read(RawData, Offset, Len);
            }
            catch
            {
                return;
            }
        }

        public static void EndDecrypt()
        {
            br.Close();
            br.Dispose();

            cs.Close();
            cs.Dispose();

            ms.Close();

            DecryptedData = ms.ToArray();

            ms.Dispose();
        }

        #endregion

        #region Disposable

        public static void Clear()
        {
            if (ms.isNull() == false)
                ms.Dispose();

            if (cs.isNull() == false)
                cs.Dispose();

            if (bw.isNull() == false)
                bw.Dispose();

            if (br.isNull() == false)
                br.Dispose();
        }

        #endregion

    }
}
